/*
 * switch-user-dialog.c
 *
 * Routines to create dialogs to switch users
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * Authors:
 *		Calvin Gaisford <cgaisford@novell.com>
 *
 * Authors for code taken from gnome-panel-logout:
 *		Vincent Untz <vuntz@gnome.org>
 *
 * Authors for code taken from gnome-session-logout:
 *		Owen Taylor <otaylor@redhat.com>
 */


#include <config.h>

#include <string.h>
#include <stdlib.h>

#include <gtk/gtk.h>
#include <gtk/gtkinvisible.h>
#include <gdk/gdkx.h>

#include <gconf/gconf-client.h>

#include <libgnome/libgnome.h>
#include <libgnomeui/libgnomeui.h>

#include "fusa-code/fusa-manager.h"
#include "libssui.h"

#define FACE_ICON_SIZE 48
#define FUS_HANDLE_TYPE	4747

enum {
	REAL_NAME_COLUMN,
	USER_NAME_COLUMN,
	DISPLAY_LABEL_COLUMN,
	ACTIVE_COLUMN,
	PIXBUF_COLUMN,
	N_COLUMNS
};


typedef struct __FusHandle
{
	gint			objType;
	GtkWidget		*user_treeview;
	GtkWidget		*vbox;
	GtkListStore	*store;
	FusaManager		*fusa_manager;
} _FusHandle;


typedef struct
{
	GtkTreeIter	iter;
	GtkWidget	*tree;
} DisplayChangedData;


static gboolean setup_treeview_idle (_FusHandle *fh);
static void setup_treeview (_FusHandle *fh);
static void populate_model (_FusHandle *fh, GtkListStore *store);
static gboolean filter_out_users (GtkTreeModel *model,
		GtkTreeIter  *iter,
		_FusHandle *fh);
static gboolean separator_func (GtkTreeModel *model,
		GtkTreeIter  *iter,
		gpointer      data);
static int compare_users (GtkTreeModel *model,
		GtkTreeIter  *a,
		GtkTreeIter  *b,
		gpointer      user_data);
static char *get_user_display_label (FusaUser *user);
static void user_displays_changed_cb (FusaUser           *user,
		DisplayChangedData *data);
static void manager_new_console_cb (FusaManager  *manager,
		FusaDisplay  *display,
		const GError *error,
		gpointer      data);
void
manager_user_added_cb(FusaManager *manager,
						FusaUser	*user,
						gpointer	data);

static GMainLoop *main_loop = NULL;

FusHandle ssui_init_fus_handle()
{
	GtkWidget		*userlist_scroller;
	_FusHandle		*fh;

	fh = malloc(sizeof(_FusHandle));
	if(fh == NULL)
		return NULL;

	memset(fh, 0, sizeof(_FusHandle));
	fh->objType = FUS_HANDLE_TYPE;

	fh->fusa_manager = fusa_manager_ref_default ();

	fh->vbox = gtk_vbox_new (FALSE, 12);
	gtk_container_set_border_width(GTK_CONTAINER (fh->vbox), 6);

	userlist_scroller = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_shadow_type(
				GTK_SCROLLED_WINDOW (userlist_scroller), GTK_SHADOW_IN);

	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (userlist_scroller),
							GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

	gtk_box_pack_start (GTK_BOX (fh->vbox), userlist_scroller, TRUE, TRUE, 0);

	// this is a guess, but for now request this size
	gtk_widget_set_size_request(GTK_WIDGET(fh->vbox), 340, 320);

	//setup_list_size_constraint (userlist_scroller, plug);

	fh->user_treeview = gtk_tree_view_new ();
	gtk_tree_view_set_headers_visible (
					GTK_TREE_VIEW (fh->user_treeview), FALSE);
	gtk_container_add (GTK_CONTAINER (userlist_scroller), 
							fh->user_treeview);

	g_idle_add ((GSourceFunc)setup_treeview_idle, fh);

	gtk_widget_show_all (fh->vbox);

	return (FusHandle)fh;
}



void ssui_free_fus_handle(FusHandle handle)
{
	_FusHandle *fh;

	fh = (_FusHandle *) handle;
	if(fh == NULL)
		return;
	
	if(fh->objType != FUS_HANDLE_TYPE)
		return;

	if(fh->fusa_manager)
		g_object_unref(fh->fusa_manager);

//	gtk_widget_destroy(fh->vbox);

	free(fh);
}


GtkWidget* ssui_fus_get_widget(FusHandle handle)
{
	_FusHandle *fh;

	fh = (_FusHandle *) handle;
	if(fh == NULL)
		return NULL;
	
	if(fh->objType != FUS_HANDLE_TYPE)
		return NULL;

	return fh->vbox;
}


gboolean ssui_fus_switch_user(FusHandle handle)
{
	_FusHandle		*fh;
	FusaDisplay      *display = NULL;
	FusaUser         *user;
	GtkTreeSelection *selection;
	GtkTreeModel     *model;
	GtkTreeIter       iter;
	GSList           *displays;
	char             *name;
	GdkScreen		*screen;

	fh = (_FusHandle *) handle;
	if(fh == NULL)
		return FALSE;
	
	if(fh->objType != FUS_HANDLE_TYPE)
		return FALSE;

	selection = gtk_tree_view_get_selection (
			GTK_TREE_VIEW (fh->user_treeview));

	// if nothing is selected, don't try to switch
	if(gtk_tree_selection_get_selected (selection,
			&model,
			&iter))
	{
		gtk_tree_model_get (model, &iter, USER_NAME_COLUMN, &name, -1);

		if (name
				&& strcmp (name, "__new_user") != 0
				&& strcmp (name, "__separator") != 0)
		{
			user = fusa_manager_get_user (fh->fusa_manager, name);
			displays = fusa_user_get_displays (user);
			if (displays)
			{
				/* FIXME: just pick the first one for now */
				display = displays->data;
			}
		}

		g_print("About to switch to user %s\n", name);

		g_free (name);

		if (gtk_widget_has_screen (fh->user_treeview))
		{
			g_print("The Tree View has a screen\n");
			screen = gtk_widget_get_screen (fh->user_treeview);
		}
		else
		{
			g_print("The Tree View does not have a screen\n");
			screen = gdk_screen_get_default ();
		}

		if (display)
		{
			g_print("Calling fusa_manager_activate_display\n");
			fusa_manager_activate_display (fh->fusa_manager, display, screen,
					manager_new_console_cb, fh, NULL);
			return TRUE;
		}

		g_print("Calling fusa_manager_new_console\n");
		fusa_manager_new_console (fh->fusa_manager, screen,
				manager_new_console_cb, fh, NULL);

		return TRUE;
	}
	else
		g_print("No selected user\n");

	return FALSE;
}


gboolean ssui_show_fusa_ui()
{
	GtkWidget		*fusaDlg;
	GtkWidget		*header_label;
	GtkWidget		*fusWidget;
	GtkWidget		*vbox;
	char			*labeltxt;
	gint			response;
	FusHandle 		fus_handle;
	gboolean		retVal = FALSE;
	gchar			*tmpstr;
	
	fus_handle = ssui_init_fus_handle();

	fusaDlg = gtk_dialog_new ();
	atk_object_set_role (gtk_widget_get_accessible (fusaDlg), 
			ATK_ROLE_ALERT);
	gtk_window_set_decorated (GTK_WINDOW (fusaDlg), FALSE);

	gtk_dialog_set_has_separator (GTK_DIALOG (fusaDlg), FALSE);

	// setup specs from Gnome HIG
	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (fusaDlg)->vbox), 12);
	gtk_container_set_border_width (GTK_CONTAINER(fusaDlg), 6);

	vbox = gtk_vbox_new (FALSE, 12);
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (fusaDlg)->vbox), 
								vbox, FALSE, FALSE, 0);
	gtk_container_set_border_width(GTK_CONTAINER (vbox), 6);
	gtk_widget_show (vbox);

	tmpstr = g_strdup_printf("<span size=\"larger\" weight=\"bold\">%s</span>",
								_("Select a User"));
	header_label = gtk_label_new (tmpstr);
	g_free (tmpstr);

	gtk_misc_set_alignment (GTK_MISC (header_label), 0, 0.5);
	gtk_label_set_use_markup (GTK_LABEL (header_label), TRUE);
	gtk_box_pack_start (GTK_BOX (vbox), header_label, FALSE, FALSE, 0);

	fusWidget = ssui_fus_get_widget(fus_handle);

	gtk_box_pack_start (GTK_BOX (vbox), fusWidget, TRUE, TRUE, 0);

	gtk_label_set_mnemonic_widget (GTK_LABEL (header_label), 
										fusWidget);

	gtk_dialog_add_button (GTK_DIALOG (fusaDlg), GTK_STOCK_CANCEL, 
			GTK_RESPONSE_CANCEL);
	gtk_dialog_add_button (GTK_DIALOG (fusaDlg), _("_Switch User"), 
			GTK_RESPONSE_OK);

	gtk_widget_show_all (fusaDlg);

	response = gtk_dialog_run (GTK_DIALOG (fusaDlg));

	if(response == GTK_RESPONSE_OK)
	{
		retVal = ssui_fus_switch_user(fus_handle);
	}

	if(retVal) {
		main_loop = g_main_loop_new(NULL, TRUE);

		while(g_main_loop_is_running(main_loop)) {
			g_main_context_iteration(NULL, TRUE);
		}

		g_main_loop_unref(main_loop);
	}

	gtk_widget_destroy (fusaDlg);

	ssui_free_fus_handle(fus_handle);

	return retVal;
}



gboolean
setup_treeview_idle (_FusHandle *fh)
{
	setup_treeview (fh);

	return FALSE;
}




void
setup_treeview (_FusHandle *fh)
{
	GtkListStore      *store;
	GtkTreeViewColumn *column;
	GtkCellRenderer   *renderer;
	GtkTreeModel      *filter;

	store = gtk_list_store_new (N_COLUMNS,
			G_TYPE_STRING,
			G_TYPE_STRING,
			G_TYPE_STRING,
			G_TYPE_BOOLEAN,
			GDK_TYPE_PIXBUF);

	populate_model (fh, store);

	filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL);

	gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
			(GtkTreeModelFilterVisibleFunc) filter_out_users,
			fh,
			NULL);

	gtk_tree_view_set_model (GTK_TREE_VIEW (fh->user_treeview),
			filter);

	g_object_unref (store);
	g_object_unref (filter);

	renderer = gtk_cell_renderer_pixbuf_new ();
	column = gtk_tree_view_column_new_with_attributes ("Image", renderer,
			"pixbuf", PIXBUF_COLUMN,
			NULL);
	gtk_tree_view_append_column (GTK_TREE_VIEW (fh->user_treeview), column);

	renderer = gtk_cell_renderer_text_new ();
	column = gtk_tree_view_column_new_with_attributes ("Name", renderer,
			"markup", DISPLAY_LABEL_COLUMN,
			NULL);
	gtk_tree_view_append_column (GTK_TREE_VIEW (fh->user_treeview), column);

	gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (fh->user_treeview),
			separator_func,
			GINT_TO_POINTER (USER_NAME_COLUMN),
			NULL);

	gtk_tree_view_column_set_sort_column_id (column, 0);
	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
			0,
			compare_users,
			NULL, NULL);
	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
			0,
			GTK_SORT_ASCENDING);
}




void
populate_model (_FusHandle *fh,
				GtkListStore *store)
{
	fh->store = store;

//	fusa_manager_list_users (fh->fusa_manager);

	g_signal_connect (fh->fusa_manager, "user-added",
				G_CALLBACK(manager_user_added_cb), fh);
}

void
manager_user_added_cb(FusaManager *manager,
						FusaUser	*user,
						gpointer	data)
{
	GtkTreeIter   iter;
	GSList       *users;
	GdkPixbuf    *pixbuf;
	int           icon_size = FACE_ICON_SIZE;
	GtkIconTheme *theme;
	GtkListStore *store;

	_FusHandle *fh = (_FusHandle *)data;
	store = fh->store;

	g_printf("manager_user_added_cb was called\n");

	if (gtk_widget_has_screen (fh->user_treeview))
	{
		theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (fh->user_treeview));
	}
	else
	{
		theme = gtk_icon_theme_get_default ();
	}

	pixbuf = gtk_icon_theme_load_icon (theme, "gdm", icon_size, 0, NULL);

	gboolean            is_active;
	guint               n_displays;
	DisplayChangedData *ddata;
	char               *label;


	/* skip the current user */
	if (fusa_user_get_uid (user) == getuid ()) {
		return;
	}

	n_displays = fusa_user_get_n_displays (user);
	is_active = n_displays > 0;

	pixbuf = fusa_user_render_icon (user, fh->user_treeview, icon_size);

	label = get_user_display_label (user);

	gtk_list_store_append (store, &iter);

	gtk_list_store_set (store, &iter,
			USER_NAME_COLUMN, fusa_user_get_user_name (user),
			REAL_NAME_COLUMN, fusa_user_get_display_name (user),
			DISPLAY_LABEL_COLUMN, label,
			ACTIVE_COLUMN, is_active,
			PIXBUF_COLUMN, pixbuf,
			-1);

	g_free (label);

	ddata = g_new0 (DisplayChangedData, 1);
	ddata->iter = iter;
	ddata->tree = fh->user_treeview;

/*	g_signal_connect_data (user, "displays-changed",
			G_CALLBACK (user_displays_changed_cb), ddata,
			(GClosureNotify) g_free, 0);
*/
}




gboolean
filter_out_users (GtkTreeModel *model,
		GtkTreeIter  *iter,
		_FusHandle	*fh)
{
	gboolean is_active;
	gboolean visible;
	char    *name;

	gtk_tree_model_get (model, iter,
			USER_NAME_COLUMN, &name,
			ACTIVE_COLUMN, &is_active,
			-1); 
	if (name == NULL) {
		return FALSE;
	}

	if (strcmp (name, "__new_user") == 0
			|| strcmp (name, "__separator") == 0) {
		visible = TRUE;
	} else {
		/* FIXME: do we show all users or only active ones? */
		visible = TRUE;
		/*visible = is_active;*/
	}

	g_free (name);

	return visible;
}




gboolean
separator_func (GtkTreeModel *model,
		GtkTreeIter  *iter,
		gpointer      data)
{
	int      column = GPOINTER_TO_INT (data);
	char    *text;
	gboolean is_separator;

	gtk_tree_model_get (model, iter, column, &text, -1);

	if (text && strcmp (text, "__separator") == 0) {
		is_separator = TRUE;
	} else {
		is_separator = FALSE;
	}

	g_free (text);

	return is_separator;
}




int
compare_users (GtkTreeModel *model,
		GtkTreeIter  *a,
		GtkTreeIter  *b,
		gpointer      user_data)
{
	char *name_a;
	char *name_b;
	char *label_a;
	char *label_b;
	int   result;

	gtk_tree_model_get (model, a, USER_NAME_COLUMN, &name_a, -1);
	gtk_tree_model_get (model, b, USER_NAME_COLUMN, &name_b, -1);
	gtk_tree_model_get (model, a, REAL_NAME_COLUMN, &label_a, -1);
	gtk_tree_model_get (model, b, REAL_NAME_COLUMN, &label_b, -1);

	if (! name_a) {
		return 1;
	} else if (! name_b) {
		return -1;
	}

	if (strcmp (name_a, "__new_user") == 0) {
		return -1;
	} else if (strcmp (name_b, "__new_user") == 0) {
		return 1;
	} else if (strcmp (name_a, "__separator") == 0) {
		return -1;
	} else if (strcmp (name_b, "__separator") == 0) {
		return 1;
	}

	if (! label_a) {
		return 1;
	} else if (! label_b) {
		return -1;
	}

	result = strcmp (label_a, label_b);

	g_free (label_a);
	g_free (label_b);
	g_free (name_a);
	g_free (name_b);

	return result;
}




char *
get_user_display_label (FusaUser *user)
{
	char *label;

	if(fusa_user_get_n_displays (user) > 0)
	{
		label = g_strdup_printf ("<big>%s</big>\n<span foreground=\"grey\">%s</span>",
			fusa_user_get_display_name (user),
			_("Logged in"));
	}
	else
	{
		label = g_strdup_printf ("<big>%s</big>",
			fusa_user_get_display_name (user));
	}
	return label;
}




void
user_displays_changed_cb (FusaUser           *user,
		DisplayChangedData *data)
{
	const char   *name;
	gboolean      is_active;
	int           n_displays;
	GdkPixbuf    *pixbuf;
	int           icon_size = FACE_ICON_SIZE;
	GtkTreeModel *filter_model;
	GtkTreeModel *model;         char         *label;
	name = fusa_user_get_user_name (user);
	n_displays = fusa_user_get_n_displays (user);
	is_active = n_displays > 0;
	pixbuf = fusa_user_render_icon (user, data->tree, icon_size);
	label = get_user_display_label (user);

	filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (data->tree));
	model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));

	gtk_list_store_set (GTK_LIST_STORE (model), &data->iter,
			USER_NAME_COLUMN, name,
			REAL_NAME_COLUMN, fusa_user_get_display_name (user),                            DISPLAY_LABEL_COLUMN, label,
			ACTIVE_COLUMN, is_active,
			PIXBUF_COLUMN, pixbuf,
			-1);
	g_free (label);
}

void
manager_new_console_cb (FusaManager  *manager,
		FusaDisplay  *display,
		const GError *error,
		gpointer      data)
{
	g_printf("manager callback called\n");
	g_main_loop_quit(main_loop);

//	GCond *cond = data;
//	g_cond_signal(cond);
}



gboolean ssui_switch_user_dialog(GdkScreen *screen, int monitor, 
								gboolean a11y_enabled)
{
	GtkWidget		*fusaDlg;
	GtkWidget		*header_label;
	GtkWidget		*fusWidget;
	GtkWidget		*vbox;
	char			*labeltxt;
	gint			response;
	FusHandle 		fus_handle;
	gboolean		retVal = FALSE;
	gchar			*tmpstr;
	
	fus_handle = ssui_init_fus_handle();

	/* Only create a managed window if a11y is enabled */
	if (!a11y_enabled)
	{
		fusaDlg = g_object_new (GTK_TYPE_DIALOG,
				"type", GTK_WINDOW_POPUP,
				NULL);
	}
	else
	{
		fusaDlg = gtk_dialog_new ();
		atk_object_set_role (gtk_widget_get_accessible (fusaDlg), 
				ATK_ROLE_ALERT);
		gtk_window_set_decorated (GTK_WINDOW (fusaDlg), FALSE);
	}

	gtk_dialog_set_has_separator (GTK_DIALOG (fusaDlg), FALSE);

	// setup specs from Gnome HIG
	gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (fusaDlg)->vbox), 12);
	gtk_container_set_border_width (GTK_CONTAINER(fusaDlg), 6);

	vbox = gtk_vbox_new (FALSE, 12);
	gtk_box_pack_start (GTK_BOX (GTK_DIALOG (fusaDlg)->vbox), 
								vbox, FALSE, FALSE, 0);
	gtk_container_set_border_width(GTK_CONTAINER (vbox), 6);
	gtk_widget_show (vbox);

	tmpstr = g_strdup_printf("<span size=\"larger\" weight=\"bold\">%s</span>",
								_("Select a User"));
	header_label = gtk_label_new (tmpstr);
	g_free (tmpstr);

	gtk_misc_set_alignment (GTK_MISC (header_label), 0, 0.5);
	gtk_label_set_use_markup (GTK_LABEL (header_label), TRUE);
	gtk_box_pack_start (GTK_BOX (vbox), header_label, FALSE, FALSE, 0);

	fusWidget = ssui_fus_get_widget(fus_handle);

	gtk_box_pack_start (GTK_BOX (vbox), fusWidget, TRUE, TRUE, 0);

	gtk_label_set_mnemonic_widget (GTK_LABEL (header_label), 
										fusWidget);

	gtk_dialog_add_button (GTK_DIALOG (fusaDlg), GTK_STOCK_CANCEL, 
			GTK_RESPONSE_CANCEL);
	gtk_dialog_add_button (GTK_DIALOG (fusaDlg), _("_Switch User"), 
			GTK_RESPONSE_OK);

	gtk_window_set_screen (GTK_WINDOW (fusaDlg), screen);
	gsm_center_window_on_screen (GTK_WINDOW (fusaDlg), screen, monitor);
//	gtk_window_set_policy (GTK_WINDOW (fusaDlg), FALSE, FALSE, TRUE);

	gtk_widget_show_all (fusaDlg);

	if (!a11y_enabled)
	{
		/* Move the grabs to our message box */
		gdk_pointer_grab (fusaDlg->window, TRUE, 0,
				NULL, NULL, GDK_CURRENT_TIME);
		gdk_keyboard_grab (fusaDlg->window, FALSE, GDK_CURRENT_TIME);
		XSetInputFocus (GDK_DISPLAY (),
				GDK_WINDOW_XWINDOW (fusaDlg->window),
				RevertToParent,
				CurrentTime);
	}

	response = gtk_dialog_run (GTK_DIALOG (fusaDlg));

	if(response == GTK_RESPONSE_OK)
	{
		retVal = ssui_fus_switch_user(fus_handle);
	}

	if(retVal) {
		main_loop = g_main_loop_new(NULL, TRUE);

		while(g_main_loop_is_running(main_loop)) {
			g_main_context_iteration(NULL, TRUE);
		}

		g_main_loop_unref(main_loop);
	}

	gtk_widget_destroy (fusaDlg);

	ssui_free_fus_handle(fus_handle);

	return retVal;
}
